//Extern g_root from converter.cpp (the root node in the scene)
extern FbxNode* g_root;
extern FbxScene* g_scene;


extern bool g_usingDualQuaternions;

//Extern function from converter.h to get ID of the node from the "node map"
extern int GetNodeID(FbxNode* a_node);
extern FbxAMatrix GetPoseMatrix(FbxPose* a_pose, int a_poseIndex);

extern FbxAMatrix GetGlobalPosition(FbxNode* a_node, FbxPose* a_pose, FbxAMatrix* a_parentGlobalPosition);

extern FbxAMatrix GetGeometry(FbxNode* a_node);

extern void SetDualQuaternion(FbxAMatrix a_matrix, double* a_dest);
extern void SetDualQuaternion(FbxDualQuaternion a_dualQuat, double* a_dest);

extern void SetMatrix(FbxAMatrix a_matrix, double* a_dest);

extern FbxQuaternion CreateQuaternion( FbxVector4 a_rotation, bool a_isEuler);

extern void AddStatusMessage(const wchar_t* a_message);

class Bone
{
	public:
		//The amount of link indices
		int m_linkIndicesCount;
		//The index pointing to mesh vertex in vertices array
		int* m_linkIndices;
		//The value it affects each linkIndex, so as many weightvalues as m_linkIndicesCount
		double* m_weightValues;
		//Name of bone
		FbxString m_name;
		int m_id;
		//Name of parent bone
		FbxString m_parentName;
		int m_parentID;

		bool m_usingDualQuat;

		//The transform matrix 4x4
		double* m_transformMatrix;

		double* m_transformDualQuat;
		//The Transform Matrix in fbx file in the cluster
		double* m_bindTransform;
		//The TransformLink Matrix in the fbx file in the cluster
		double* m_boneTransformLink;
	
		Bone()
		{
			m_name = "";
			m_linkIndicesCount = 0;
			m_parentName = "";
			m_parentID = -1;

			if (g_usingDualQuaternions)
			{
				m_transformDualQuat = new double[8];
				m_bindTransform = new double[8];				
			}
			else
			{
				m_transformMatrix = new double[16];
				m_bindTransform = new double[16];				
			}	
			
		}

		void Init(FbxCluster* a_cluster, FbxMesh* a_mesh)
		{
			FbxNode* f_node = a_cluster->GetLink();
			FbxNode* f_parentNode = f_node->GetParent();			

			m_name = f_node->GetName();
			m_id = GetNodeID(f_node);

			//Check if parentNode exists and that it's not root node
			if (f_parentNode != NULL && f_parentNode != g_root )
			{
				m_parentName = f_parentNode->GetName();
				m_parentID = GetNodeID(f_parentNode);
			}			

			m_linkIndicesCount = a_cluster->GetControlPointIndicesCount();

			//These two arrays are needed because the pointer gets destroyed, another way is to destroy the scene after exporting
			int* f_linkIndices = a_cluster->GetControlPointIndices();
			double* f_weightValues = a_cluster->GetControlPointWeights();	

			m_linkIndices = new int[m_linkIndicesCount];
			m_weightValues = new double[m_linkIndicesCount];

			for (int i = 0; i < m_linkIndicesCount; i++)
			{
				m_linkIndices[i] = f_linkIndices[i];

				m_weightValues[i] = f_weightValues[i];
			}	

			SetGlobalPositionMatrix(f_node, a_cluster, a_mesh);		
		}

	private:
		void SetGlobalPositionMatrix(FbxNode* a_node, FbxCluster* a_cluster, FbxMesh* a_mesh)
		{			
			//The final matrix
			FbxAMatrix f_transformMatrix;	
			
			FbxCluster::ELinkMode lClusterMode = a_cluster->GetLinkMode();

			FbxAMatrix f_referenceGlobalInitPosition;
			FbxAMatrix f_referenceGlobalCurrentPosition;
			FbxAMatrix f_associateGlobalInitPosition;
			FbxAMatrix f_associateGlobalCurrentPosition;
			FbxAMatrix f_clusterGlobalInitPosition;
			FbxAMatrix f_clusterGlobalCurrentPosition;

			FbxAMatrix f_referenceGeometry;
			FbxAMatrix f_associateGeometry;
			FbxAMatrix f_clusterGeometry;

			FbxAMatrix f_clusterRelativeInitPosition;
			FbxAMatrix f_clusterRelativeCurrentPositionInverse;

			//Global position Matrix
			FbxAMatrix f_globalPosition;
			FbxAMatrix f_globalParentPosition;

			FbxAMatrix f_geometryOffset;
			FbxAMatrix f_globalOffPosition;

			//Get the scene so the poses are retrievable
			//FbxScene* f_scene = a_cluster->GetScene();		
			int f_poseCount = g_scene->GetPoseCount();

			for (int i = 0; i < f_poseCount; i++)
			{
				FbxPose* f_pose = g_scene->GetPose(i);	
				
				//I'd use != -1, but the examples use > -1, so maybe there's some special cases where nodeIndex is -2 or something
				if (f_pose->Find(a_node) > -1)
				{

					FbxAMatrix f_matrix = GetPoseMatrix( f_pose, f_pose->Find( a_node ) );

					FbxNode* f_meshNode;

					if (a_mesh->GetNodeCount() > 0)
					{
						f_meshNode = a_mesh->GetNode();
						//Calculate the global position of the mesh I guess!
						f_globalPosition = GetGlobalPosition(f_meshNode, f_pose, &f_globalParentPosition );
						f_geometryOffset = GetGeometry( f_meshNode );
						f_globalOffPosition = f_globalPosition * f_geometryOffset;
						f_referenceGeometry = GetGeometry( f_meshNode );
					}
					/*else
					{
						//f_meshNode = a_node;						
					}*/	

					

					
					a_cluster->GetTransformMatrix(f_referenceGlobalInitPosition);
					//Seemed to get an idendity matrix when I had f_globalPosition
					f_referenceGlobalCurrentPosition = f_globalOffPosition;
					// Multiply lReferenceGlobalInitPosition by Geometric Transformation
					
					f_referenceGlobalInitPosition *= f_referenceGeometry;

					// Get the link initial global position and the link current global position.
					a_cluster->GetTransformLinkMatrix(f_clusterGlobalInitPosition);
					//a_pose parameter is NULL because result will be identity matrix if it isn't
					f_clusterGlobalCurrentPosition = GetGlobalPosition( a_node, NULL, NULL);

					// Compute the initial position of the link relative to the reference.
					f_clusterRelativeInitPosition = f_clusterGlobalInitPosition.Inverse() * f_referenceGlobalInitPosition;

					// Compute the current position of the link relative to the reference.
					f_clusterRelativeCurrentPositionInverse = f_referenceGlobalCurrentPosition.Inverse() * f_clusterGlobalCurrentPosition;

					// Compute the shift of the link relative to the reference.
					f_transformMatrix = f_referenceGlobalCurrentPosition.Inverse();//f_clusterRelativeCurrentPositionInverse * f_clusterRelativeInitPosition;
					//Break the loop since no other pose should enter this!		
					break;				
				}
				
			}

			if (g_usingDualQuaternions)
			{	
				SetDualQuaternion(f_transformMatrix, m_transformDualQuat);
				//clusterInitPosition.inverse * referenceInitPosition
				SetDualQuaternion(f_clusterRelativeInitPosition, m_bindTransform);
				
			}
			else
			{
				SetMatrix(f_transformMatrix, m_transformMatrix);				
			}

			
		}
};